package com.rickpan.service;

import com.rickpan.dto.share.ShareAccessRequest;
import com.rickpan.dto.share.ShareAccessResponse;
import com.rickpan.entity.FileInfo;
import com.rickpan.entity.FileShare;
import com.rickpan.entity.ShareAccessLog;
import com.rickpan.entity.User;
import com.rickpan.exception.BusinessException;
import com.rickpan.repository.FileInfoRepository;
import com.rickpan.repository.FileShareRepository;
import com.rickpan.repository.ShareAccessLogRepository;
import com.rickpan.repository.UserRepository;
import com.rickpan.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 分享安全验证服务
 * 
 * @author RickPan Team
 * @since 2025-01-05
 */
@Service
public class ShareSecurityService {

    private static final Logger log = LoggerFactory.getLogger(ShareSecurityService.class);

    @Autowired
    private FileShareRepository fileShareRepository;

    @Autowired
    private FileInfoRepository fileInfoRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ShareAccessLogRepository accessLogRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Value("${app.share.rate-limit.max-requests:100}")
    private int maxRequestsPerHour;

    @Value("${app.base-url:http://localhost:8080}")
    private String baseUrl;

    /**
     * 验证分享访问权限
     */
    @Transactional
    public ShareAccessResponse validateAccess(String shareCode, ShareAccessRequest request, HttpServletRequest httpRequest) {
        log.info("验证分享访问: shareCode={}", shareCode);

        try {
            // 1. 获取分享信息
            FileShare fileShare = fileShareRepository.findByShareCode(shareCode)
                    .orElse(null);

            if (fileShare == null) {
                return logAndReturnFailure(null, getClientIP(httpRequest), getUserAgent(httpRequest), 
                                         ShareAccessLog.ActionType.VIEW, "分享不存在");
            }

            // 2. 检查分享状态
            if (fileShare.getStatus() != FileShare.ShareStatus.ACTIVE) {
                return logAndReturnFailure(fileShare.getId(), getClientIP(httpRequest), getUserAgent(httpRequest),
                                         ShareAccessLog.ActionType.VIEW, "分享已被禁用");
            }

            // 3. 检查过期时间
            if (fileShare.isExpired()) {
                // 自动更新过期状态
                fileShare.setStatus(FileShare.ShareStatus.EXPIRED);
                fileShareRepository.save(fileShare);
                
                return logAndReturnFailure(fileShare.getId(), getClientIP(httpRequest), getUserAgent(httpRequest),
                                         ShareAccessLog.ActionType.VIEW, "分享已过期");
            }

            // 4. 检查访问次数限制
            if (fileShare.isAccessLimitReached()) {
                return logAndReturnFailure(fileShare.getId(), getClientIP(httpRequest), getUserAgent(httpRequest),
                                         ShareAccessLog.ActionType.VIEW, "访问次数已达上限");
            }

            // 5. 检查访问频率限制
            String clientIP = getClientIP(httpRequest);
            if (isRateLimited(shareCode, clientIP)) {
                return logAndReturnFailure(fileShare.getId(), clientIP, getUserAgent(httpRequest),
                                         ShareAccessLog.ActionType.VIEW, "访问过于频繁，请稍后再试");
            }

            // 6. 验证密码
            if (fileShare.getShareType() == FileShare.ShareType.PASSWORD) {
                if (request.getPassword() == null || 
                    !passwordEncoder.matches(request.getPassword(), fileShare.getPassword())) {
                    return logAndReturnFailure(fileShare.getId(), clientIP, getUserAgent(httpRequest),
                                             ShareAccessLog.ActionType.VIEW, "密码错误");
                }
            }

            // 7. 获取文件信息
            FileInfo fileInfo = fileInfoRepository.findById(fileShare.getFileId())
                    .orElseThrow(() -> new BusinessException("文件不存在"));

            // 8. 增加访问次数
            fileShare.incrementAccessCount();
            fileShareRepository.save(fileShare);

            // 9. 记录成功访问日志
            ShareAccessLog.ActionType actionType = "DOWNLOAD".equals(request.getAction()) ? 
                    ShareAccessLog.ActionType.DOWNLOAD : ShareAccessLog.ActionType.VIEW;
            
            ShareAccessLog accessLog = ShareAccessLog.createSuccessLog(
                    fileShare.getId(), clientIP, getUserAgent(httpRequest), actionType);
            accessLogRepository.save(accessLog);

            // 10. 生成临时访问令牌
            String accessToken = generateAccessToken(shareCode);

            // 11. 构建成功响应
            return buildSuccessResponse(fileShare, fileInfo, accessToken);

        } catch (Exception e) {
            log.error("分享访问验证失败: shareCode={}", shareCode, e);
            return ShareAccessResponse.failure("访问验证失败: " + e.getMessage());
        }
    }

    /**
     * 检查访问频率限制
     */
    private boolean isRateLimited(String shareCode, String clientIP) {
        String key = String.format("share:rate_limit:%s:%s", shareCode, clientIP);
        
        try {
            // 使用Redis计数器实现简单的频率限制
            String countStr = (String) redisTemplate.opsForValue().get(key);
            int count = countStr != null ? Integer.parseInt(countStr) : 0;
            
            if (count >= maxRequestsPerHour) {
                return true;
            }
            
            // 增加计数
            redisTemplate.opsForValue().increment(key);
            redisTemplate.expire(key, Duration.ofHours(1));
            
            return false;
        } catch (Exception e) {
            log.warn("频率限制检查失败: {}", e.getMessage());
            return false; // 出错时不限制访问
        }
    }

    /**
     * 生成临时访问令牌
     */
    private String generateAccessToken(String shareCode) {
        String token = UUID.randomUUID().toString();
        String key = "share:access_token:" + token;
        
        // 令牌有效期1小时
        redisTemplate.opsForValue().set(key, shareCode, Duration.ofHours(1));
        
        return token;
    }

    /**
     * 验证访问令牌
     */
    public String validateAccessToken(String token) {
        String key = "share:access_token:" + token;
        return (String) redisTemplate.opsForValue().get(key);
    }

    // 私有辅助方法

    private ShareAccessResponse logAndReturnFailure(Long shareId, String clientIP, String userAgent,
                                                   ShareAccessLog.ActionType actionType, String errorMessage) {
        // 记录失败访问日志
        if (shareId != null) {
            ShareAccessLog failureLog = ShareAccessLog.createFailureLog(
                    shareId, clientIP, userAgent, actionType, errorMessage);
            accessLogRepository.save(failureLog);
        }
        
        return ShareAccessResponse.failure(errorMessage);
    }

    private ShareAccessResponse buildSuccessResponse(FileShare fileShare, FileInfo fileInfo, String accessToken) {
        // 获取分享者信息
        String sharerName = userRepository.findById(fileShare.getUserId())
                .map(User::getUsername)
                .orElse("未知用户");

        // 构建权限信息
        ShareAccessResponse.PermissionInfo permissions = new ShareAccessResponse.PermissionInfo(
                fileShare.getAllowPreview(), fileShare.getAllowDownload());

        // 构建文件信息
        ShareAccessResponse.FileInfo fileInfoDto = new ShareAccessResponse.FileInfo(
                fileInfo.getId(), fileInfo.getOriginalName(), fileInfo.getFileSize(),
                fileInfo.getMimeType(), sharerName);

        // 构建URL
        String previewUrl = fileShare.getAllowPreview() ? 
                String.format("%s/api/shares/%s/preview?token=%s", baseUrl, fileShare.getShareCode(), accessToken) : null;
        String downloadUrl = fileShare.getAllowDownload() ? 
                String.format("%s/api/shares/%s/download?token=%s", baseUrl, fileShare.getShareCode(), accessToken) : null;

        return ShareAccessResponse.success(accessToken, previewUrl, downloadUrl, permissions, fileInfoDto);
    }

    private String getClientIP(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIP = request.getHeader("X-Real-IP");
        if (xRealIP != null && !xRealIP.isEmpty()) {
            return xRealIP;
        }
        
        return request.getRemoteAddr();
    }

    private String getUserAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }
}
